DynamicAtlas = Class("DynamicAtlas")
local mesh = love.graphics.newMesh(4, "triangles")
mesh:setVertexMap({1, 2, 3, 1, 3, 4})
---@diagnostic disable-next-line: duplicate-set-field
function DynamicAtlas:ctor(width, height)
    width = width or DynamicAtlasDefaultSize
    height = height or DynamicAtlasDefaultSize
    self.canvas = love.graphics.newCanvas(width, height)
    self.width = width
    self.height = height

    self.spaceX = 2
    self.spaceY = 2
    self.startX = 2
    self.startY = 2
    self.maxLineHeight = 0
end

function DynamicAtlas:insertSpriteFrame(sf)
    if not self:checkInsert(sf) then
        return false
    end

    local uvs = sf.uvs
    local width, height = sf.width, sf.height
    local vertices = {
        {0, 0, uvs[1], uvs[2]},
        {0 + width, 0, uvs[3], uvs[4]},
        {0 + width, 0 + height, uvs[5], uvs[6]},
        {0, 0 + height, uvs[7], uvs[8]}
    }
    mesh:setVertices(vertices)
    mesh:setTexture(sf.image)

    self.canvas:renderTo(function()
        love.graphics.draw(mesh, self.startX, self.startY)
    end)

    local newFrame = SpriteFrame.new(self.canvas)
    newFrame.packable = false
    newFrame.isPackedFrame = true
    newFrame:setRect(self.startX, self.startY, width, height)
    sf.packedFrame = newFrame
    self.startX = self.startX + width + self.spaceX
    return newFrame
end

function DynamicAtlas:checkInsert(sf)
    local width, height = sf:getDimensions()

    local x = self.startX
    local y = self.startY
    local lineChanged = false
    if x + width + self.spaceX > self.width then
        x = self.spaceX
        y = y + self.maxLineHeight + self.spaceY
        lineChanged = true
    end

    if y + height + self.spaceY > self.height then
        return false
    end

    self.startX = x
    self.startY = y
    if lineChanged then
        self.maxLineHeight = 0
    elseif height > self.maxLineHeight then
        self.maxLineHeight = height
    end

    return x, y
end

function DynamicAtlas:insertText(text)
    if not self:checkInsert(text) then
        return false
    end
    self.canvas:renderTo(function()
        love.graphics.draw(text, self.startX, self.startY)
    end)

    local newFrame = SpriteFrame.new(self.canvas)
    newFrame.packable = false
    newFrame.isPackedFrame = true
    local width, height = text:getDimensions()
    newFrame:setRect(self.startX, self.startY, width, height)
    self.startX = self.startX + width + self.spaceX
    return newFrame
end

function DynamicAtlas:insertRect(sf)
    local width, height = sf:getDimensions()
    local radius = sf.radius
    if not self:checkInsert(sf) then
        return false
    end

    self.canvas:renderTo(function()
        -- 处理边缘误差，周围各扩展一像素
        love.graphics.rectangle("fill", self.startX - 1, self.startY - 1, width + 2, height + 2, radius)
    end)
    local newFrame = SpriteFrame.new(self.canvas)
    newFrame.packable = false
    newFrame.isPackedFrame = true
    newFrame:setRect(self.startX, self.startY, width, height)
    self.startX = self.startX + width + self.spaceX
    return newFrame
end

function DynamicAtlas:insert(item, type)
    if type == "text" then
        return self:insertText(item)
    elseif type == "rect" then
        return self:insertRect(item)
    else
        return self:insertSpriteFrame(item)
    end
end